﻿#include "CenterAccountMgr.h"
#include "CenterService.h"
#include "SceneMgr/CenterSceneMrg.h"
#include "ProtoBuffer/AllProtocol.h"
#include "Common/Utility/ServerTime.h"
#include "Common/Utility/AccountUtility.h"
#include "Common/TableData/RoleExpCfg.h"
#include "Active/ActiveCharacterMgr.h"
#include "Cache/OfflineCharacterDataMgr.h"
#include "Duplicate/CenterDuplicateMgr.h"
#include "Common/TableDataEx/MapCfg.h"
#include "Common/TableData/SensitivewordSensitive_wordCfg.h"
#include "Common/LogDefine.h"
#include "Global/GlobalMgr.h"
#include "Union/UnionMgr.h"
#include "Team/TeamManager.h"
#include "Room/RoomMgr.h"
#include "Common/Item/ItemMgr.h"
#include "MailCenter/MailManager.h"
#include "Common/ReliveMgr/ReliveMgr.h"
#include "Arena/ArenaMgr.h"
#include "Global/GlobalMgr.h"
#include "MallCenter/MallManager.h"
#include "Common/TableData/TeleportBornCfg.h"
#include "Common/TableData/DuplicateDuplicateCfg.h"

CenterAccountMgr::CenterAccountMgr()
{
	m_dbreq = 0;
	m_uidMap.clear();
	m_clientIdMap.clear();
	m_cidClientLogicIdMap.clear();
	m_bloginOpen = true;
}

CenterAccountMgr::~CenterAccountMgr()
{
	m_dbreq = 0;
	m_uidMap.clear();
	m_clientIdMap.clear();
	m_cidClientLogicIdMap.clear();
}

bool CenterAccountMgr::Init()
{
	IMessageDispatch *pMessageDispatch = g_GetCenterService()->GetServiceMessageDispatch();
	if (nullptr == pMessageDispatch)
		return false;

	m_bloginOpen = true;
	pMessageDispatch->registerServerHandler(EMODULE_ID_LOGIN, this);
	pMessageDispatch->registerDBProxyHandler(EMODULE_ID_LOGIN, this);
	pMessageDispatch->registerClientHandler(EMODULE_ID_LOGIN, this);
	return true;
}

bool CenterAccountMgr::UnInit()
{
	m_dbreq = 0;
	IMessageDispatch *pMessageDispatch = g_GetCenterService()->GetServiceMessageDispatch();
	if (nullptr == pMessageDispatch)
		return false;

	pMessageDispatch->unregisterServerHandler(EMODULE_ID_LOGIN);
	pMessageDispatch->unregisterDBProxyHandler(EMODULE_ID_LOGIN);
	pMessageDispatch->unregisterClientHandler(EMODULE_ID_LOGIN);

	return true;
}

void CenterAccountMgr::Update(uint64_t tick)
{
	int32_t difftime = 0;
	CID_GAME_MAP::iterator iter = m_clientIdMap.begin();
	CID_GAME_MAP::iterator iterEnd = m_clientIdMap.end();
	VEC_UINT32 deleteCids;
	VEC_UINT32 logoutCids;
	deleteCids.clear();

	for (; iter != iterEnd;)
	{
		//清除一直没有离线成功的角色 1分钟清除时间
		if (iter->second.state == UID_STATE_LOGOUT)
		{
			difftime = (int32_t)(tick - iter->second.stateTimeStamp);
			if (difftime > LOGOUT_TIMEOUT)
			{
				/*GateChangeLogic(iter->second.clientId, NotifyGateChangeLogic_cType_LEAVE_LOGIC, 0, true);
				m_uidMap.erase(iter->second.uid);*/
				logoutCids.push_back(iter->second.clientId);
			}
		}
		//清除一直在登陆状态的帐号(30分钟)
		else if (iter->second.state == UID_STATE_LOGIN || iter->second.state == UID_STATE_LOADCHARLIS || iter->second.state == UID_STATE_ENTER)
		{
			difftime = (int32_t)(tick - iter->second.stateTimeStamp);
			if (difftime > TOEKN_TIME)
			{
				GateChangeLogic(iter->second.clientId, NotifyGateChangeLogic_cType_LEAVE_LOGIC, 0, true, LOGOUT_FLAG_BREAK_TIMEOUT);
				m_uidMap.erase(iter->second.uid);
				deleteCids.push_back(iter->second.clientId);
			}
		}
		else if (iter->second.state == UID_STATE_DISCONNECT)
		{
			difftime = (int32_t)(tick - iter->second.stateTimeStamp);
			if (difftime > RECONNECT_TIME)
			{
				iter->second.state = UID_STATE_LOGOUT;
				//防止逻辑服没有回来中心服做账号回收
				iter->second.stateTimeStamp = tick;
				NotifyLogicLeave(iter->second.playCid, iter->second.uid, iter->second.clientId, iter->second.logicId,LOGOUT_TYPE_DIS_TIME_OUT);
				m_logoutUidMap[iter->second.uid] = iter->second.clientId;
			}
		}
		iter++;
	}

	//清除一下列表
	for (uint32_t i = 0; i < deleteCids.size(); i++)
	{
		m_clientIdMap.erase(deleteCids[i]);
		//这里的清除列表是没有登录成功的，所以下线列表是不需要清除的
		//m_logoutUidMap.erase(deleteCids[i]);
	}

	//处理一下离线列表
	for (uint32_t i = 0; i < logoutCids.size(); i++)
	{
		Logout(logoutCids[i],0);
	}
}

void CenterAccountMgr::onServerMessage(uint32_t actionId, RECV_PACKAGE &package)
{
	switch (actionId)
	{
	case LOGIC_LOGIN_CENTER_RSP:
		EnterGameRsp(package);
		break;
	case LOGIC_LOGINOUT_CNETER_RSP:
		LeaveGameRsp(package);
		break;
	case LOGIC_TO_CENTER_KICK_PLAYER:
		LogicKickPlayer(package);
		break;
	default:
		LogErrFmtPrint("center account Mgr didnt recv this msg, cmd:%d, moduleId:%d", actionId, package.moduleId);
		break;
	}
}

void CenterAccountMgr::onClientMessage(uint32_t actionId, RECV_PACKAGE& package, uint64_t Id /* = 0 */)
{
	if (package.session == nullptr)
	{
		LogErrFmtPrint(" CenterAccountMgr::onClientMessage, session null, module:%d, cmd:%d", package.moduleId, package.cmd);
		return;
	}
	if (!IsAllPreLoadFinished())
	{
		LogErrFmtPrint("CenterAccountMgr::onClientMessage...IsAllPreLoadFinished false...module:%d, cmd:%d", package.moduleId, package.cmd);
		return;
	}
	switch (actionId)
	{
	case CLIENT_TO_CENTER_LOGIN:
		LoginGateReq((uint32_t)Id, package);
		break;
	case CLIENT_TO_CENTER_CREATE_CHARACTER:
		CreateCharacterReq((uint32_t)Id, package);
		break;
	case CLIENT_TO_CENTER_DEL_CHARACTER:
		DelCharacterReq((uint32_t)Id, package);
		break;
	case CLIENT_TO_CENTER_LEAVE_GAME:
		LeaveGameReq((uint32_t)Id, package);
		break;
	case CLIENT_TO_CENTER_ENTER_GAME:
		EnterGameReq((uint32_t)Id, package);
		break;
	case CLIENT_TO_CENTER_RECONNECT_REQ:
		ReConnectReq((uint32_t)Id, package);
		break;
	case CLIENT_TO_CENTER_DEL_CHARACTER_CANCEL:
		DelCharacterCancelReq((uint32_t)Id, package);
		break;
	default:
		break;
	}

}

void CenterAccountMgr::onDBProxyMessage(uint32_t actionId, RECV_PACKAGE &package)
{
	switch (actionId)
	{
	case DBPROXY_PROTOCOL_GET_CHARACTER_LIST_RSP:
		CharLstRspFromDB(package);
		break;
	case DBPROXY_PROTOCOL_DEL_CHARACTER_RSP:
		DelCharacterRsp(package);
		break;
	case DBPROXY_PROTOCOL_DEL_CHARACTER_CANCEL_RSP:
		DelCharacterCancelRsp(package);
		break;
	case DBPROXY_PROTOCOL_CREATE_CHARACTER_RSP:
		CreateCharacterRsp(package);
		break;
	default:
		break;
	}
}

uint32_t CenterAccountMgr::NewReqId()
{
	if (m_dbreq >= UINT32_MAX)
	{
		m_dbreq = 0;
	}
	++m_dbreq;
	return m_dbreq;
}

UidGameInfo* CenterAccountMgr::GetUidGameInfo(uint32_t clientId)
{
	CID_GAME_MAP::iterator iter = m_clientIdMap.find(clientId);
	CID_GAME_MAP::iterator iterEnd = m_clientIdMap.end();

	if (iter == iterEnd)
		return nullptr;

	return &iter->second;
}

UidGameInfo* CenterAccountMgr::GetUidGameInfoByCid(CharIDType cid)
{
	CID_CLIENT_LOGIC_ID_MAP::iterator iterCid = m_cidClientLogicIdMap.find(cid);
	CID_CLIENT_LOGIC_ID_MAP::iterator iterCidEnd = m_cidClientLogicIdMap.end();
	if (iterCid == iterCidEnd)
		return nullptr;
	CID_GAME_MAP::iterator iter = m_clientIdMap.find(iterCid->second.clientId);
	CID_GAME_MAP::iterator iterEnd = m_clientIdMap.end();
	if (iter == iterEnd)
		return nullptr;

	return &iter->second;
}

uint32_t CenterAccountMgr::GetClientId(uint32_t uid)
{
	UID_GAME_MAP::iterator iter = m_uidMap.find(uid);
	if (iter != m_uidMap.end())
	{
		return iter->second;
	}

	return 0;
}

uint32_t CenterAccountMgr::GetLogoutClientId(uint32_t uid)
{
	UID_GAME_MAP::iterator iter = m_logoutUidMap.find(uid);
	if (iter != m_logoutUidMap.end())
	{
		return iter->second;
	}

	return 0;
}

bool CenterAccountMgr::LoginGateReq(uint32_t clientId, RECV_PACKAGE& package)
{
	bool result = false;
	bool retCode = false;


	ClientLoginGateReq clogin;
	uint32_t zid = g_GetCenterService()->GetZoneId();
	{
		retCode = clogin.ParseFromArray(package.dataAfterHead, package.sizeAfterHead);
		MMOLOG_PROCESS_ERROR(retCode);

		if (!m_bloginOpen)
		{
			//防止服务器初始化或停服的时候 白名单的人或利用客户端bug进入的人进来 打乱服务器加载数据
			LogErrFmtPrint("[center] CenterAccountMgr::LoginGateReq server loginOpen false..clientid:%u, uid:%u", clientId, clogin.uid());
			return false;
		}

		uint32_t uid = clogin.uid();
		//fix by lian
		//先通过uid查找一下之前是否有clientid在登录着 如果有需要对旧的clientid做挤号操作
		//注意这里不能用uid作为多角色的唯一映射 否则会有异步问题 
		uint32_t oldClientId = GetClientId(uid);
		UidGameInfo* oldUid = GetUidGameInfo(oldClientId);

		if (g_GetCenterService()->TokenTimeCheck())
		{
			uint64_t SecTime = Time::Now().UnixMSec();
			MMOLOG_PROCESS_ERROR((SecTime - clogin.logintimestamp()) <= MAX_CENTER_LOGIN_TIME_SEC);
		}

		string srvToken = g_MakeMd5(uid, clogin.logintimestamp());
		MMOLOG_PROCESS_ERROR(srvToken == clogin.token());

		LogDebugFmtPrint("[center] CenterAccountMgr::LoginGateReq, uid%d, clientId:%d", uid, clientId);
		uint64_t tick = g_GetGlobalServerTime()->Tick();
		//重复登陆
		if (oldUid)
		{
			//如果是同一个连接在不断的请求发该协议 那么进行过滤
			if (clientId == oldClientId)
			{
				LogErrFmtPrint("[center] CenterAccountMgr::LoginGateReq but same player send same cmd...clientid:%u, oldclientid:%u, uid:%u, cid:%llu, logicid:%u, state:%d ", clientId, oldClientId, oldUid->uid, oldUid->playCid, oldUid->logicId, oldUid->state);
				return false;
			}

			//如果旧的游戏角色不处于断线状态 那么需要将旧角色的客户端session断开
			if (oldUid->state != UID_STATE_DISCONNECT)
			{
				//强制断开之前的客户端session
				retCode = GateChangeLogic(oldUid->clientId, NotifyGateChangeLogic_cType_LEAVE_LOGIC, 0, true, LOGOUT_FLAG_REPLACE);
				MMO_PROCESS_ERROR(retCode);
			}

			//掉线重登或者被挤, 通知逻辑服退出
			//如果旧角色处于进入游戏 游戏中 切换场景中 断线中 四种状态那么需要通知旧角色从逻辑服退出 并且需要将oldUid作移除操作
			if (oldUid->state == UID_STATE_ENTER ||
				oldUid->state == UID_STATE_GAMING ||
				oldUid->state == UID_STATE_SWITCH ||
				oldUid->state == UID_STATE_DISCONNECT)
			{
				LogDebugFmtPrint("[center] CenterAccountMgr::LoginGateReq...same account login,notify logic kick player logout.. uid:%u, kick_player_cid:%llu, logic_id:%u,  clientid:%u, oldclientid:%u,state:%u", oldUid->uid, oldUid->playCid, oldUid->logicId, clientId, oldClientId, oldUid->state);
				NotifyLogicLeave(oldUid->playCid, oldUid->uid, oldUid->clientId, oldUid->logicId, LOGOUT_TYPE_KICK_OUT);
				oldUid->state = UID_STATE_LOGOUT;
				//防止逻辑服没有回来中心服做账号回收
				oldUid->stateTimeStamp = tick;
				//将在离线的角色clientId放到离线列表中
				m_logoutUidMap[uid] = oldUid->clientId;
			}

			//如果是旧账号 并且旧账号处于 登录或者向DB请求角色列表状态那么直接删除m_cidMap中旧账号的记录
			if (oldUid && (oldUid->state == UID_STATE_LOGIN || oldUid->state == UID_STATE_LOADCHARLIS))
			{
				m_clientIdMap.erase(oldUid->clientId);
			}
		}

		//以上情况都需要将m_uidMap先做下移除操作 最算之前没有旧的账号也可以先做下移除操作
		m_uidMap.erase(uid);
		
		//保存新登录上来的clientId的UidGameInfo信息
		UidGameInfo* pNewUid = &m_clientIdMap[clientId];
		pNewUid->tokenTimeStamp = tick;
		pNewUid->state = UID_STATE_LOGIN;
		pNewUid->token = srvToken;
		pNewUid->uid = clogin.uid();
		pNewUid->clientId = clientId;
		pNewUid->stateTimeStamp = tick;
		pNewUid->logicId = 0;
		pNewUid->playCid = 0;
		pNewUid->gateId = clogin.gateid();
		pNewUid->channelId = clogin.channelid();

		//保存最新登录的角色的clientId
		m_uidMap[uid] = clientId;

		//获取正在下线的UidInfo
		uint32_t logoutClientId = GetLogoutClientId(uid);
		UidGameInfo* logoutUid = GetUidGameInfo(logoutClientId);
		//如果没有旧角色正在下线中 那么直接去请求加载角色列表 如果有旧的账号 那么需要旧账号完全离开游戏后才去加载角色列表
		if (!logoutUid || logoutUid->state != UID_STATE_LOGOUT)
		{
			retCode = CharLstReqToDB(zid, uid, clientId);
			MMOLOG_PROCESS_ERROR(retCode);
		}

		result = true;
	}

Exit0:
	return result;
}

bool CenterAccountMgr::CharLstReqToDB(uint32_t zid, uint32_t uid, uint32_t clientId)
{
	bool result = false;
	bool retCode = false;
	char setStr[64];
	memset(setStr, 64, 0);

	CharacterListRequest charListReq;
	charListReq.set_requestid((uint64_t)uid);
	charListReq.set_groupid(zid);
	charListReq.set_db_req_id(clientId);
	charListReq.set_uid(uid);
	retCode = g_GetCenterService()->SendDataToDBProxy(EMODULE_ID_LOGIN, DBPROXY_PROTOCOL_GET_CHARACTER_LIST_REQ, &charListReq);
	MMOLOG_PROCESS_ERROR(retCode);
	result = true;
Exit0:
	return result;
}

bool CenterAccountMgr::CharLstRspFromDB(RECV_PACKAGE& package)
{
	bool result = false;
	bool retCode = false;

	{
		CharacterListResponse charLstrsp;
		charLstrsp.add_charinfolist();
		retCode = charLstrsp.ParseFromArray(package.dataAfterHead, package.sizeAfterHead);
		MMOLOG_PROCESS_ERROR(retCode);

		uint32_t uid = (uint32_t)charLstrsp.requestid();
		uint32_t reqClientId = charLstrsp.db_req_id();
		uint32_t clientId = GetClientId(uid);
		UidGameInfo* pUid = GetUidGameInfo(clientId);

		//这种情况表示有新的角色登录上来准备进行挤号操作了 这种情况直接返回 客户端不会收到任何跟账号相关的角色摘要数据
		//这种情况直接把旧的连接断开
		if (clientId != 0 && clientId != reqClientId)
		{
			GateChangeLogic(reqClientId, NotifyGateChangeLogic_cType_LEAVE_LOGIC, 0, true, LOGOUT_FLAG_REPLACE);
			LogErrFmtPrint("[center] CenterAccountMgr::CharLstRspFromDB....pUid->reqid != reqid....uid:%u ,clientid:%u, reqClientId:%u, state:%d, logicid:%u ", uid, clientId, reqClientId, 0, 0);
			MMO_PROCESS_ERROR(false);
		}

		//如果找不到则视为出错
		if (pUid == nullptr)
		{
			MMOLOG_FMT_ERROR("LoginGateRsp, getuidGameInfo null, uid:%d, zid:%d", uid, g_GetCenterService()->GetZoneId());
			MMO_PROCESS_ERROR(false);
		}

		//再加一下状态判断 状态出错直接断开连接
		if (pUid->state != UID_STATE_LOGIN)
		{
			GateChangeLogic(pUid->clientId, NotifyGateChangeLogic_cType_LEAVE_LOGIC, 0, true, LOGOUT_FLAG_KICK);
			LogErrFmtPrint("[center] CenterAccountMgr::CharLstRspFromDB....but pUid->state!=UID_STATE_LOGIN....uid:%u ,clientid:%u, reqClientId:%u, state:%d, logicid:%u ", uid, pUid->clientId, reqClientId, pUid->state, pUid->logicId);
			MMO_PROCESS_ERROR(false);
		}

		ClientLoginGateRsp loginrsp;
		loginrsp.set_ret(ClientLoginGateRsp_RESULT_LOGIN_OK);

		CharacterDBSimpleInfo *pcharSimpleDB = nullptr;
		//CharacterDBInfo charDB;
		for (int i = 0; i < charLstrsp.charinfolist_size(); i++)
		{
			pcharSimpleDB = loginrsp.add_charinfolist();
			pcharSimpleDB->CopyFrom(charLstrsp.charinfolist(i));
			//charDB = charLstrsp.charinfolist(i);
			//CharDBToCharSimpleDB(&charDB, pcharSimpleDB);

			CidCurAndLastScene cidScene;
			cidScene.sceneId = charLstrsp.charinfolist(i).enter_scene_id();
			cidScene.mapId = charLstrsp.charinfolist(i).enter_map_id();
			cidScene.x = charLstrsp.charinfolist(i).enterposx();
			cidScene.y = charLstrsp.charinfolist(i).enterposy();
			cidScene.z = charLstrsp.charinfolist(i).enterposz();
			cidScene.lastSceneId = charLstrsp.charinfolist(i).lastsceneid();
			cidScene.lastMapId = charLstrsp.charinfolist(i).lastmapid();
			cidScene.lastx = charLstrsp.charinfolist(i).lastposx();
			cidScene.lasty = charLstrsp.charinfolist(i).lastposy();
			cidScene.lastz = charLstrsp.charinfolist(i).lastposz();
			cidScene.curstate = charLstrsp.charinfolist(i).curstate();
			cidScene.killValue = charLstrsp.charinfolist(i).killvalue();
			cidScene.camp = charLstrsp.charinfolist(i).camp();
			cidScene.race = charLstrsp.charinfolist(i).race();
			if (charLstrsp.charinfolist(i).has_guide_dup())
			{
				cidScene.finishStep = charLstrsp.charinfolist(i).guide_dup().finish_step();
				cidScene.finishFlag = charLstrsp.charinfolist(i).guide_dup().finish_flag();
			}

			pUid->cidSceneIdMap[charLstrsp.charinfolist(i).charid()] = cidScene;
		}

		//标记一下拉取角色列表完毕
		pUid->state = UID_STATE_LOADCHARLIS;
		pUid->stateTimeStamp = g_GetGlobalServerTime()->Tick();
		retCode = g_GetCenterService()->SendDataToClientUnchecked(pUid->gateId, pUid->clientId, EMODULE_ID_LOGIN, CENTER_TO_CLIENT_LOGIN, &loginrsp);
		MMOLOG_PROCESS_ERROR(retCode);

		LogDebugFmtPrint("[center] CenterAccountMgr::CharLstRspFromDB client login  account ok, uid:%d", uid);
		result = true;
	}
Exit0:
	return result;
}

bool CenterAccountMgr::CreateCharacterReq(uint32_t clientId, RECV_PACKAGE& package)
{
	bool result = false;
	bool retCode = false;

	ClientCreateCharacterReq createCharacter;
	uint32_t createResult = RET_SUCCESS;
	uint32_t uid = 0;
	UidGameInfo* pUid = nullptr;
	{
		retCode = createCharacter.ParseFromArray(package.dataAfterHead, package.sizeAfterHead);
		MMOLOG_PROCESS_ERROR(retCode);

		uid = createCharacter.uid();
		pUid = GetUidGameInfo(clientId);
		//uint32_t newClientId = GetClientId(uid);
		if (pUid == nullptr)
		{
			MMOLOG_FMT_ERROR("create characterReq, canntfind uidgameinfo, uid:%d", uid);
			MMO_PROCESS_ERROR(false);
		}

		//创建角色的账号必须处于拉取角色列表完毕状态
		if (pUid->state != UID_STATE_LOADCHARLIS || pUid->playCid > 0)
		{
			MMOLOG_FMT_ERROR("create characterReq faild but pUid->state != UID_STATE_LOGIN || pUid->playCid > 0, uid:%d, state%d, clientId%d, pUid->clientId:%d", uid, pUid->state, clientId, pUid->clientId);
			MMO_PROCESS_ERROR(false);
		}

		if (pUid->cidSceneIdMap.size() >= CHARACTER_MAX)
		{
			createResult = RET_LOGIN_CHARACTER_NUM_LIMIT;
			MMOLOG_FMT_ERROR("create character over max");
			MMO_PROCESS_ERROR(false);
		}

		size_t name_len = createCharacter.name().size();
		if (name_len < CHARACTER_NAME_MIN_LENGTH || name_len >= CHARACTER_NAME_MAX_LENGTH)
		{
			createResult = RET_LOGIN_CHARACTER_NAME_LEN_ERROR;
			MMOLOG_FMT_ERROR("create character name length error");
			MMOLOG_PROCESS_ERROR(false);
		}

		//检查名字合法性
		const SensitivewordSensitive_wordCfgMap * psensitiveConfigMap = g_GetSensitivewordSensitive_wordCfgTable()->GetSensitivewordSensitive_wordCfgMap();
		if (psensitiveConfigMap)
		{
			SensitivewordSensitive_wordCfgMap::const_iterator iter = psensitiveConfigMap->begin();
			for (; iter != psensitiveConfigMap->end();++iter)
			{
				if (createCharacter.name().find(iter->second.sensitiveWord) != std::string::npos)
				{
					createResult = RET_LOGIN_CHARACTER_ILLEGAL;
					MMOLOG_FMT_ERROR("create character name string illegal error");
					MMOLOG_PROCESS_ERROR(false);
				}
			}
		}


		uint32_t prof = createCharacter.prof();
		uint32_t sex = createCharacter.sex();
		if (!ECharacterSex_IsValid(sex))
		{
			createResult = RET_LOGIN_CHARACTER_SEX_ERROR;
			MMOLOG_FMT_ERROR("create character sex valid");
			MMO_PROCESS_ERROR(false);
		}
		if (!ECharacterProf_IsValid(prof))
		{
			createResult = RET_LOGIN_CHARACTER_PROF_ERROR;
			MMOLOG_FMT_ERROR("create character prof valid");
			MMO_PROCESS_ERROR(false);
		}

		retCode = CreateCharacterToDBReq(uid, clientId, createCharacter, pUid->channelId);
		MMO_PROCESS_ERROR(retCode);
	}

	result = true;
Exit0:
	if (createResult != RET_SUCCESS)
	{
		if (pUid != nullptr)
		{
			ClientCreateCharacterRsp clientRsp;
			clientRsp.set_result(createResult);
			result = g_GetCenterService()->SendDataToClientUnchecked(pUid->gateId, clientId, EMODULE_ID_LOGIN, CENTER_TO_CLIENT_CREATE_CHARACTER, &clientRsp);
		}
		else
			LogErrFmtPrint("CreateCharacterReq, cannt send data to gate pUid == null,clientid:%d", clientId);
	}

	return result;
}

bool CenterAccountMgr::CreateCharacterToDBReq(uint32_t uid, uint32_t clientId, ClientCreateCharacterReq& createCharacter, uint32_t channelId)
{
	bool result = false;
	bool retCode = false;
	{
		uint32_t prof = createCharacter.prof();
		uint32_t sex = createCharacter.sex();
		uint32_t zid = g_GetCenterService()->GetZoneId();

		const RoleBornCfgInfo *pRoleBornCfg = g_GetRoleBornCfg((uint8_t)prof, (uint8_t)sex);
		MMOLOG_PROCESS_ERROR(pRoleBornCfg != nullptr);

		const Point3<float> *pBornPos = g_GetMapCfgMgr()->RandBornPoint(pRoleBornCfg->mapID);
		MMOLOG_PROCESS_ERROR(nullptr != pBornPos);

		uint64_t tick = Time::Now().UnixSec(); 

		CreateCharacterRequest dbCreateCharReq;
		dbCreateCharReq.set_name(createCharacter.name());
		dbCreateCharReq.set_groupid(zid);
		dbCreateCharReq.set_requestid(uid);
		dbCreateCharReq.set_clientid(clientId);
		dbCreateCharReq.set_uid(uid);
		dbCreateCharReq.set_channelid(channelId);
		dbCreateCharReq.set_zonemailid(g_GetMailMgr()->GetZoneMailId());

		CharacterDBInfo* pCharDBInfo = dbCreateCharReq.mutable_charinfo();
		pCharDBInfo->set_charid(0);
		pCharDBInfo->set_groupid(zid);

		CharacterDBBaseInfo* pCharDBBaseInfo = pCharDBInfo->mutable_basedata();
		pCharDBBaseInfo->set_createtime(tick);
		pCharDBBaseInfo->set_lastsavetime(0);
		pCharDBBaseInfo->set_onlinetime(0);
		pCharDBBaseInfo->set_logintime(0);
		pCharDBBaseInfo->set_logouttime(0);

		pCharDBBaseInfo->set_name(createCharacter.name());
		pCharDBBaseInfo->set_sex(sex);
		pCharDBBaseInfo->set_prof(prof);
		pCharDBBaseInfo->set_race(pRoleBornCfg->race);
		pCharDBBaseInfo->set_level(pRoleBornCfg->bornLevel);
		pCharDBBaseInfo->set_exp(0);
		pCharDBBaseInfo->set_hp(0);
		pCharDBBaseInfo->set_fightpower(1);
		pCharDBBaseInfo->set_gold(pRoleBornCfg->gold);
		pCharDBBaseInfo->set_bindgold(pRoleBornCfg->bindGold);
		pCharDBBaseInfo->set_diamond(pRoleBornCfg->diamond);
		pCharDBBaseInfo->set_binddiamond(pRoleBornCfg->bindDiamond);

		pCharDBBaseInfo->set_enter_scene_id((uint32_t)pRoleBornCfg->mapID);
		pCharDBBaseInfo->set_enter_map_id((uint32_t)pRoleBornCfg->mapID);
		pCharDBBaseInfo->set_enterposx(pBornPos->x);
		pCharDBBaseInfo->set_enterposy(pBornPos->y);
		pCharDBBaseInfo->set_enterposz(pBornPos->z);

		//和初始场景和地图设置一样
		pCharDBBaseInfo->set_lastsceneid(pRoleBornCfg->mapID);
		pCharDBBaseInfo->set_lastmapid(pRoleBornCfg->mapID);
		pCharDBBaseInfo->set_lastposx(pBornPos->x);
		pCharDBBaseInfo->set_lastposy(pBornPos->y);
		pCharDBBaseInfo->set_lastposz(pBornPos->z);

		//初始装备
		g_GetItemMgr()->CreateCharEquip(*pCharDBInfo);

		const RoleExpCfgInfo *pExpCfg = nullptr;
		pExpCfg = g_GetRoleExpCfgTable()->GetRoleExpCfgInfo(pRoleBornCfg->bornLevel);
		if (nullptr == pExpCfg)
		{
			MMOLOG_FMT_ERROR("[logic] client to logic create character....nullptr == pExpCfg.. bornLevel:%d ", pRoleBornCfg->bornLevel);
		}
		MMOLOG_PROCESS_ERROR(nullptr != pExpCfg);

		//初始体力
		pCharDBBaseInfo->set_stamina(pExpCfg->stamina);

		retCode = g_GetCenterService()->SendDataToDBProxy(EMODULE_ID_LOGIN, DBPROXY_PROTOCOL_CREATE_CHARACTER_REQ, &dbCreateCharReq);
		MMOLOG_PROCESS_ERROR(retCode);
	}

	result = true;
Exit0:
	return result;
}

bool CenterAccountMgr::CreateCharacterRsp(RECV_PACKAGE& package)
{
	bool result = false;
	bool retCode = false;
	uint32_t clientId = 0;
	uint32_t reqClientId = 0;
	UidGameInfo* pUid = nullptr;

	ClientCreateCharacterRsp clientCreateRsp;
	CreateCharacterResponse dbCreateRsp;
	{
		retCode = dbCreateRsp.ParseFromArray(package.dataAfterHead, package.sizeAfterHead);
		MMOLOG_PROCESS_ERROR(retCode);

		//这里会出现一下情况 就是aa账号中的请求创角 但是在DB还没返回该协议的过程中 另外一个人也用aa账号登录
		//在挤掉后上一个人登录的aa账号后 上一个人请求创角的DB协议返回 此时会将创建的角色信息返回给最新登录的aa账号 这种情况从设计上应该没问题
		uint32_t uid = (uint32_t)dbCreateRsp.requestid();
		reqClientId = dbCreateRsp.clientid();
		clientId = GetClientId(uid);
		clientCreateRsp.set_result(dbCreateRsp.result());

		pUid = GetUidGameInfo(clientId);
		if (pUid == nullptr)
		{
			MMOLOG_FMT_ERROR("create characterRsp, cannt find uid info, uid:%d, cientId:%d", uid, 0);
			MMO_PROCESS_ERROR(false);
		}

		CharacterDBSimpleInfo simpleCharInfo = dbCreateRsp.charinfo();
		if (dbCreateRsp.result() == RET_SUCCESS)
		{
			uint64_t charId = simpleCharInfo.charid();
			//创建角色,只要数据库返回创建成功了，证明这个角色是一定存在了，需要加到离线数据中
			if (!g_GetOfflineCharacterDataMgr()->AddOfflineCharacter(charId, simpleCharInfo, pUid->channelId))
			{
				LogErrFmtPrint("[center] CenterAccountMgr::CreateCharacterRsp......AddOfflineCharacter failed...charId:%llu ", charId);
			}

			//创角日志记录 创角日志需要放在此处 因为有可能DB还没返回玩家下线了
			PlayerCreateLog(uid, simpleCharInfo.charid(), pUid->channelId, simpleCharInfo);

			if (reqClientId != clientId)
			{
				MMOLOG_FMT_ERROR("db create characterReq faild but reqClientId != clientId reqClientId=%d, clientId=%d", reqClientId, clientId);
				MMO_PROCESS_ERROR(false);
			}

			//创建角色的账号必须处于登录状态
			if (pUid->state != UID_STATE_LOADCHARLIS || pUid->playCid > 0)
			{
				MMOLOG_FMT_ERROR("db create characterReq faild but pUid->state != UID_STATE_LOGIN || pUid->playCid > 0, uid:%d, state%d, clientId%d, pUid->clientId:%d", uid, pUid->state, clientId, pUid->clientId);
				//MMOLOG_FMT_ERROR("create characterReq faild but pUid->state != UID_STATE_LOGIN || pUid->playCid > 0, uid:%d", uid);
				MMO_PROCESS_ERROR(false);
			}

			CidCurAndLastScene cidScene;

			cidScene.sceneId = simpleCharInfo.enter_scene_id();
			cidScene.mapId = simpleCharInfo.enter_map_id();
			cidScene.x = simpleCharInfo.enterposx();
			cidScene.y = simpleCharInfo.enterposy();
			cidScene.z = simpleCharInfo.enterposz();
			cidScene.lastSceneId = simpleCharInfo.lastsceneid();
			cidScene.lastMapId = simpleCharInfo.lastmapid();
			cidScene.lastx = simpleCharInfo.lastposx();
			cidScene.lasty = simpleCharInfo.lastposy();
			cidScene.lastz = simpleCharInfo.lastposz();
			cidScene.curstate = simpleCharInfo.curstate();
			cidScene.killValue = simpleCharInfo.killvalue();
			cidScene.camp = simpleCharInfo.camp();
			cidScene.race = simpleCharInfo.race();
			if (simpleCharInfo.has_guide_dup())
			{
				cidScene.finishStep = simpleCharInfo.guide_dup().finish_step();
				cidScene.finishFlag = simpleCharInfo.guide_dup().finish_flag();
			}
			pUid->cidSceneIdMap[simpleCharInfo.charid()] = cidScene;


			//CharacterDBSimpleInfo *pSimpleCharDBInfo = new CharacterDBSimpleInfo();
			//CharDBToCharSimpleDB(&charInfo, pSimpleCharDBInfo);
			//clientCreateRsp.set_allocated_newcharacterinfo(pSimpleCharDBInfo);
			clientCreateRsp.mutable_newcharacterinfo()->CopyFrom(simpleCharInfo);


			LogDebugFmtPrint("[center] create charatcter succeed,uid=%llu,charId:%llu ", dbCreateRsp.requestid(),charId);
		}
	}
	//请求相同的情况下才通知
	if (reqClientId == clientId)
	{
		if (pUid != nullptr)
		{
			retCode = g_GetCenterService()->SendDataToClientUnchecked(pUid->gateId, clientId, EMODULE_ID_LOGIN, CENTER_TO_CLIENT_CREATE_CHARACTER, &clientCreateRsp);
			MMO_PROCESS_ERROR(retCode);
		}
		else
			LogErrFmtPrint("CreateCharacterRsp, cannt send data to gate pUid == null,clientid:%d", clientId);

	}
	result = true;
	

Exit0:
	return result;
}

bool CenterAccountMgr::CharDBToCharSimpleDB(CharacterDBInfo* pCharInfo, CharacterDBSimpleInfo* charSimpleInfo)
{
	bool result = false;
	MMOLOG_PROCESS_ERROR(pCharInfo != NULL);
	{
		const CharacterDBBaseInfo& baseInfo = pCharInfo->basedata();
		charSimpleInfo->set_charid(pCharInfo->charid());
		charSimpleInfo->set_name(baseInfo.name());
		charSimpleInfo->set_sex(baseInfo.sex());
		charSimpleInfo->set_prof(baseInfo.prof());
		charSimpleInfo->set_level(baseInfo.level());
		charSimpleInfo->set_race(baseInfo.race());
		charSimpleInfo->set_exp(baseInfo.exp());
		charSimpleInfo->set_hp(baseInfo.hp());
		charSimpleInfo->set_fightpower(baseInfo.fightpower());
		charSimpleInfo->set_official(baseInfo.official());
		charSimpleInfo->set_knight(baseInfo.knight());
		charSimpleInfo->set_camp(baseInfo.camp());

		CharFacadeProto *facade = charSimpleInfo->mutable_facade();
		MMOLOG_PROCESS_ERROR(facade != NULL);

		const CharacterDBEquipData &protoEquip = pCharInfo->equipdata();
		g_GetGlobalMgr()->SetFacade(protoEquip, *facade);
	}
	result = true;
Exit0:
	return result;
}

bool CenterAccountMgr::DelCharacterReq(uint32_t clientId, RECV_PACKAGE& package)
{
	bool result = false;
	bool retCode = false;
	UidGameInfo* pUid = nullptr;

	MMOLOG_PROCESS_ERROR(package.session != nullptr);
	
	{
		uint32_t zid = g_GetCenterService()->GetZoneId();

		ClientDelCharacterReq delReq;
		retCode = delReq.ParseFromArray(package.dataAfterHead, package.sizeAfterHead);
		MMO_PROCESS_ERROR(retCode);
		uint32_t uid = delReq.uid();

		uint64_t delCharID = delReq.charid();

		pUid = GetUidGameInfo(clientId);
		if (pUid == nullptr)
		{
			MMOLOG_FMT_ERROR("DelCharacterReq, cannt find uidinfo, uid:%d, clientId:%d", uid,clientId);
			MMO_PROCESS_ERROR(false);
		}

		//删除角色的账号必须处于DB角色列表已经加载状态
		if (pUid->state != UID_STATE_LOADCHARLIS || pUid->playCid > 0)
		{
			MMOLOG_FMT_ERROR("create characterReq faild but pUid->state != UID_STATE_LOGIN || pUid->playCid > 0, uid:%d", uid);
			MMO_PROCESS_ERROR(false);
		}

		CIDSCENE::iterator iter = pUid->cidSceneIdMap.find(delCharID);
		CIDSCENE::iterator iterEnd = pUid->cidSceneIdMap.end();
		if (iter == iterEnd)
		{
			MMOLOG_FMT_ERROR("DelCharacterReq, cannt find delcharId, in uidInfo, delcharId:%llu, uid:%d", delCharID, uid);
			MMO_PROCESS_ERROR(false);
		}

		//请求数据库 删除角色
		CharacterDelDBRequst req;
		req.set_requestid((uint64_t)uid);
		req.set_charid(delCharID);
		req.set_groupid(zid);
		req.set_clientid(clientId);
		req.set_uid(uid);

		retCode = g_GetCenterService()->SendDataToDBProxy(EMODULE_ID_LOGIN, DBPROXY_PROTOCOL_DEL_CHARACTER_REQ, &req);
		MMOLOG_PROCESS_ERROR(retCode);
	}

	result = true;
Exit0:
	if (result == false)
	{
		if (pUid != nullptr)
		{
			ClientDelCharacterRsp rsp;
			rsp.set_retcode(RET_FAIL);
			g_GetCenterService()->SendDataToClientUnchecked(pUid->gateId, clientId, EMODULE_ID_LOGIN, CENTER_TO_CLIENT_DEL_CHARACTER, &rsp);
		}
		else
			LogErrFmtPrint("DelCharacterReq, cannt send data to gate pUid == null,clientid:%d", clientId);
	}

	return result;
}

bool CenterAccountMgr::DelCharacterCancelReq(uint32_t clientId, RECV_PACKAGE& package)
{
	bool result = false;
	bool retCode = false;
	UidGameInfo* pUid = nullptr;

	MMOLOG_PROCESS_ERROR(package.session != nullptr);

	{
		uint32_t zid = g_GetCenterService()->GetZoneId();

		ClientDelCharacterCancelReq delReq;
		retCode = delReq.ParseFromArray(package.dataAfterHead, package.sizeAfterHead);
		MMO_PROCESS_ERROR(retCode);
		uint32_t uid = delReq.uid();

		uint64_t delCharID = delReq.charid();

		pUid = GetUidGameInfo(clientId);
		if (pUid == nullptr)
		{
			MMOLOG_FMT_ERROR("DelCharacterReqcancel, cannt find uidinfo, uid:%d, clientId:%d", uid, clientId);
			MMO_PROCESS_ERROR(false);
		}

		//删除角色的账号必须处于DB角色列表已经加载状态
		if (pUid->state != UID_STATE_LOADCHARLIS || pUid->playCid > 0)
		{
			MMOLOG_FMT_ERROR("create characterReq faild but pUid->state != UID_STATE_LOGIN || pUid->playCid > 0, uid:%d", uid);
			MMO_PROCESS_ERROR(false);
		}

		CIDSCENE::iterator iter = pUid->cidSceneIdMap.find(delCharID);
		CIDSCENE::iterator iterEnd = pUid->cidSceneIdMap.end();
		if (iter == iterEnd)
		{
			MMOLOG_FMT_ERROR("DelCharacterReqcancel, cannt find delcharId, in uidInfo, delcharId:%llu, uid:%d", delCharID, uid);
			MMO_PROCESS_ERROR(false);
		}

		//请求数据库 删除角色
		CharacterDelDBCancleRequst req;
		req.set_requestid((uint64_t)uid);
		req.set_charid(delCharID);
		req.set_groupid(zid);
		req.set_clientid(clientId);
		req.set_uid(uid);

		retCode = g_GetCenterService()->SendDataToDBProxy(EMODULE_ID_LOGIN, DBPROXY_PROTOCOL_DEL_CHARACTER_CANCEL_REQ, &req);
		MMOLOG_PROCESS_ERROR(retCode);
	}

	result = true;
Exit0:
	if (result == false)
	{
		if (pUid != nullptr)
		{
			ClientDelCharacterCancelRsp rsp;
			rsp.set_retcode(RET_FAIL);
			g_GetCenterService()->SendDataToClientUnchecked(pUid->gateId, clientId, EMODULE_ID_LOGIN, CENTER_TO_CLIENT_DEL_CHARACTER, &rsp);
		}
		else
			LogErrFmtPrint("DelCharacterReqcancel, cannt send data to gate pUid == null,clientid:%d", clientId);
	}

	return result;
}

bool CenterAccountMgr::DelCharacterRsp(RECV_PACKAGE& package)
{
	bool result = false;
	bool retCode = false;

	CharacterDelDBResponse rsp;
	{
		retCode = rsp.ParseFromArray(package.dataAfterHead, package.sizeAfterHead);
		MMO_PROCESS_ERROR(retCode);
		//这里会出现一下情况 就是aa账号中的请求删角 但是在DB还没返回该协议的过程中 另外一个人也用aa账号登录
		//在挤掉后上一个人登录的aa账号后 上一个人请求创角的DB协议返回 此时会将删除的角色信息返回给最新登录的aa账号 这种情况从设计上应该没问题
		uint32_t uid = (uint32_t)rsp.requestid();
		uint32_t reqClientId = rsp.clientid();
		uint32_t clientId = GetClientId(uid);
		uint64_t charId = rsp.charid();

		if (rsp.requestid() == 0)   //此值为0，表示是DB服主动推过来的，才是真正的删除帐号
		{
			//删除角色，只要数据库返回成功，证明角色一定不存在了,中心服一定要回调删除成功
			if (RET_SUCCESS == rsp.retcode())
			{
				g_GetActiveCharacterMgr()->OnCharacterDel(charId);
				LogInfoFmtPrint("[center] DelCharacterRsp...uid:%u, charid:%lu, clientid:%u,reqClientId:%u ", uid, charId, clientId, reqClientId);
			}
		}

		UidGameInfo* pUid = GetUidGameInfo(clientId);
		if (pUid == nullptr)
		{
			MMOLOG_FMT_ERROR("DelCharacterRsp, cannt find uidInfo, uid:%d, charId:%llu", uid, charId);
			MMO_PROCESS_ERROR(false);
		}

		//创建角色的账号必须处于登录状态
		if (pUid->state != UID_STATE_LOADCHARLIS || pUid->playCid > 0)
		{
			MMOLOG_FMT_ERROR("create characterReq faild but pUid->state != UID_STATE_LOGIN || pUid->playCid > 0, uid:%d", uid);
			MMO_PROCESS_ERROR(false);
		}
		


		OfflineCharacterData *pOffline = g_GetOfflineCharacterDataMgr()->GetOfflineCharacter(charId);
		MMOLOG_PROCESS_ERROR(pOffline != nullptr);
		string charName = pOffline->GetCharName();

		ClientDelCharacterRsp clientRsp;
		clientRsp.set_retcode(rsp.retcode());
		if (RET_SUCCESS == rsp.retcode())
		{
			clientRsp.set_charid(charId);
			//pUid->cidSceneIdMap.erase(charId);
			PlayerDelCharacterLog(pUid->uid, charId, pUid->channelId, charName);
		}

		

		//如果请求的跟当前在线的一致才做通知操作
		if (reqClientId == clientId)
		{
			retCode = g_GetCenterService()->SendDataToClientUnchecked(pUid->gateId, pUid->clientId, EMODULE_ID_LOGIN, CENTER_TO_CLIENT_DEL_CHARACTER, &clientRsp);
			MMO_PROCESS_ERROR(retCode);
		}
	}

	result = true;
Exit0:
	return result;
}


bool CenterAccountMgr::DelCharacterCancelRsp(RECV_PACKAGE& package)
{
	bool result = false;
	bool retCode = false;

	CharacterDelDBCancelResponse rsp;
	{
		retCode = rsp.ParseFromArray(package.dataAfterHead, package.sizeAfterHead);
		MMO_PROCESS_ERROR(retCode);
		//这里会出现一下情况 就是aa账号中的请求删角 但是在DB还没返回该协议的过程中 另外一个人也用aa账号登录
		//在挤掉后上一个人登录的aa账号后 上一个人请求创角的DB协议返回 此时会将删除的角色信息返回给最新登录的aa账号 这种情况从设计上应该没问题
		uint32_t uid = (uint32_t)rsp.requestid();
		uint32_t reqClientId = rsp.clientid();
		uint32_t clientId = GetClientId(uid);
		uint64_t charId = rsp.charid();


		UidGameInfo* pUid = GetUidGameInfo(clientId);
		if (pUid == nullptr)
		{
			MMOLOG_FMT_ERROR("DelCharacterRsp, cannt find uidInfo, uid:%d, charId:%llu", uid, charId);
			MMO_PROCESS_ERROR(false);
		}

		//创建角色的账号必须处于登录状态
		if (pUid->state != UID_STATE_LOADCHARLIS || pUid->playCid > 0)
		{
			MMOLOG_FMT_ERROR("create characterReq faild but pUid->state != UID_STATE_LOGIN || pUid->playCid > 0, uid:%d", uid);
			MMO_PROCESS_ERROR(false);
		}



		OfflineCharacterData *pOffline = g_GetOfflineCharacterDataMgr()->GetOfflineCharacter(charId);
		MMOLOG_PROCESS_ERROR(pOffline != nullptr);
		string charName = pOffline->GetCharName();

		ClientDelCharacterRsp clientRsp;
		clientRsp.set_retcode(rsp.retcode());
		if (RET_SUCCESS == rsp.retcode())
		{
			clientRsp.set_charid(charId);
			PlayerDelCancelCharacterLog(pUid->uid, charId, pUid->channelId, charName);
		}

		//如果请求的跟当前在线的一致才做通知操作
		if (reqClientId == clientId)
		{
			retCode = g_GetCenterService()->SendDataToClientUnchecked(pUid->gateId, pUid->clientId, EMODULE_ID_LOGIN, CENTER_TO_CLIENT_DEL_CHARACTER_CANCEL, &clientRsp);
			MMO_PROCESS_ERROR(retCode);
		}
	}

	result = true;
Exit0:
	return result;
}


bool CenterAccountMgr::EnterGameReq(uint32_t clientId, RECV_PACKAGE& package)
{
	bool result = false;
	bool retCode = false;
	MMO_PROCESS_ERROR(package.session != nullptr);

	//进入游戏的几种情况
	//情况1  假如在角色A进入游戏中角色B登录上来发生了挤号，那么会对该接口会对角色A和角色B的clientId进行比较，如果发现不一致，那么直接不让A进入游戏了
	//情况2  假如在角色A进入游戏后才发生了挤号操作 那么此时角色A的状态会被设置为UID_STATE_ENTER状态 此时如果B角色登录上来 那么会调用NotifyLogicLeave接口通知对应的逻辑服让角色A下线 并且直接通知网关把角色A关闭 不让角色A继续发送协议上来

	{
		EnterGame reqPack;
		retCode = reqPack.ParseFromArray(package.dataAfterHead, package.sizeAfterHead);
		MMO_PROCESS_ERROR(retCode);
		uint32_t uid = reqPack.uid();

		CharIDType charId = reqPack.charid();
		uint32_t newClientId = GetClientId(uid);

		//这里如果出现为空 那么说明是有号挤上来了 然后旧的账号的UidGameInfo被移除了
		UidGameInfo* pUid = GetUidGameInfo(clientId);
		if (pUid == nullptr)
		{
			MMOLOG_FMT_ERROR("[center] EnterGameReq, cannt find uidInfo, uid:%d,  charId:%llu", uid, charId);
			return false;
		}

		//如果进入游戏的clientId跟最新的clientId不同 表示当前有账号在挤号操作 这种情况直接不让该角色进入游戏 并且断开网络
		if (clientId != newClientId)
		{
			GateChangeLogic(pUid->clientId, NotifyGateChangeLogic_cType_LEAVE_LOGIC, 0, true,LOGOUT_FLAG_KICK);
			MMOLOG_FMT_ERROR("[center] EnterGameReq fail because oldClientId != newClientId, oldClientId:%d, newClientId:%d,uid:%u, charid:%lu,state:%d ,playcid:%lu ", clientId, newClientId, uid, charId, pUid->state, pUid->playCid);
			MMO_PROCESS_ERROR(false);
		}

		//保证登录的账号处于 已经加载DB角色列表返回的状态 并且该该号没有角色登录中， 这种情况直接断开网络
		if (pUid->state != UID_STATE_LOADCHARLIS || pUid->playCid != 0)
		{
			GateChangeLogic(pUid->clientId, NotifyGateChangeLogic_cType_LEAVE_LOGIC, 0, true);
			MMOLOG_FMT_ERROR("[center] EnterGameReq fail because pUid->state != UID_STATE_LOADCHARLIS || pUid->playCid != 0, uid:%u,charid:%lu, oldClientId:%d, newClientId:%d, state:%d ,playcid:%lu ",uid,charId, clientId, newClientId,pUid->state,pUid->playCid);
			MMO_PROCESS_ERROR(false);
		}

		CIDSCENE::iterator iterChar = pUid->cidSceneIdMap.find(charId);
		if (iterChar == pUid->cidSceneIdMap.end())
		{
			MMOLOG_FMT_ERROR("[center] EnterGameReq, can not find char scene and map,  charId:%llu , uid:%u ", charId, uid);
			return false;
		}
		CidCurAndLastScene &curScene = iterChar->second;
		uint32_t entersceneId = curScene.sceneId;
		uint32_t entermapId = curScene.mapId;
		float enterx = curScene.x;
		float entery = curScene.y;
		float enterz = curScene.z;
		uint32_t dstLogicId = 0;
		Point3<float> curpos(enterx, entery, enterz);
		uint32_t killValue = curScene.killValue;
		int32_t camp = curScene.camp;
		uint32_t race = curScene.race;
		uint32_t finishStep = curScene.finishStep;
		uint32_t maxStep = g_GetCenterDuplicateMgr()->GetGuideMaxStep();
		uint32_t finishFlag = curScene.finishFlag;
		uint32_t guideDupId = 0;
		CreateSceneParamProto paramProto;
		paramProto.set_param_type(ESceneParamType_None);
		//如果新手引导副本没有完成或者上次完成的步骤小于最大步骤，需要再次进入到新手副本中
		if (!finishFlag || finishStep < maxStep)
		{
			//下一个步骤
			uint32_t nextStep = finishStep + 1;
			//根据种族和步骤获取副本出生点信息
			const GuideStepCfgInfo *pGuideStepCfg = g_GetCenterDuplicateMgr()->GetGuideStepCfg(race, nextStep);
			if (nullptr == pGuideStepCfg)
			{
				MMOLOG_FMT_ERROR("[center] EnterGameReq, nullptr == pGuideStepCfg,  charId:%llu , uid:%u,race:%u,nextStep:%u ", charId, uid,race,nextStep);
				return false;
			}
			const DuplicateDuplicateCfgInfo *pDupCfg = g_GetDuplicateDuplicateCfgTable()->GetDuplicateDuplicateCfgInfo(pGuideStepCfg->nDupId);
			if (nullptr == pDupCfg)
			{
				MMOLOG_FMT_ERROR("[center] EnterGameReq, nullptr == pDupCfg,  charId:%llu , uid:%u,race:%u,nextStep:%u,dupid:%u ", charId, uid, race, nextStep,pGuideStepCfg->nDupId);
				return false;
			}
			const TeleportBornCfgInfo *pBornCfg = g_GetTeleportBornCfgTable()->GetTeleportBornCfgInfo(pGuideStepCfg->bornId);
			if (nullptr == pBornCfg)
			{
				MMOLOG_FMT_ERROR("[center] EnterGameReq, nullptr == pBornCfg,  charId:%llu , uid:%u,race:%u,nextStep:%u,bornid:%u ", charId, uid, race, nextStep,pGuideStepCfg->bornId);
				return false;
			}
			if (pDupCfg->sceneResource != pBornCfg->scene)
			{
				MMOLOG_FMT_ERROR("[center] EnterGameReq, pDupCfg->sceneResource != pBornCfg->scene,  charId:%llu , uid:%u,race:%u,nextStep:%u,bornid:%u,bornmap:%u,dupmsp:%u ", charId, uid, race, nextStep, pGuideStepCfg->bornId,pBornCfg->scene,pDupCfg->sceneResource);
				return false;
			}
			const SBornPosCfgInfo *pBornPosCfg = g_GetMapCfgMgr()->GetBornCfgInfo(pBornCfg->scene, pGuideStepCfg->bornId);
			if (nullptr == pBornPosCfg)
			{
				MMOLOG_FMT_ERROR("[center] EnterGameReq, nullptr == pBornPosCfg,  charId:%llu , uid:%u,race:%u,nextStep:%u,bornid:%u,map:%u ", charId, uid, race, nextStep, pGuideStepCfg->bornId,pBornCfg->scene);
				return false;
			}
			uint32_t logicId = g_GetCenterSceneMgr()->GetLogicIdByMapId(pBornCfg->scene);
			if (logicId <= 0)
			{
				MMOLOG_FMT_ERROR("[center] EnterGameReq, logicId <= 0,  charId:%llu , uid:%u,race:%u,nextStep:%u,bornid:%u,map:%u ", charId, uid, race, nextStep, pGuideStepCfg->bornId, pBornCfg->scene);
				return false;
			}
			//
			dupInfo *pDupInfo = g_GetCenterDuplicateMgr()->enterGuideDup(charId, pDupCfg->duplicateID);
			if (nullptr == pDupInfo)
			{
				MMOLOG_FMT_ERROR("[center] EnterGameReq, nullptr == pDupInfo,  charId:%llu , uid:%u,race:%u,nextStep:%u,bornid:%u,map:%u ", charId, uid, race, nextStep, pGuideStepCfg->bornId, pBornCfg->scene);
				return false;
			}
			//副本参数
			paramProto.set_param_type(ESceneParamType_Dup);
			paramProto.set_char_id(pDupInfo->cid);
			paramProto.set_duplicate_id(pDupInfo->dupId);
			paramProto.set_key_type(pDupInfo->keyType);
			paramProto.set_key_value(pDupInfo->keyValue);
			paramProto.set_finish_step(finishStep);
			//目标逻辑服ID
			dstLogicId = logicId;
			//新手副本ID
			guideDupId = pDupInfo->dupId;
			//
			entersceneId = pDupInfo->sceneId;
			entermapId = pBornCfg->scene;
			enterx = pBornPosCfg->bornpos.x;
			entery = pBornPosCfg->bornpos.y;
			enterz = pBornPosCfg->bornpos.z;
		}
		else
		{
			//如果上次下线是所在的地图是动态地图，需要副本这边先处理下
			if (g_GetMapCfgMgr()->IsDynamic(entermapId))
			{
				if (!g_GetCenterDuplicateMgr()->loginCanEnter(entersceneId, charId))
				{
					//不能再进入动态地图了，需要重置地图和坐标为上一次的地图和坐标
					entersceneId = curScene.lastSceneId;
					entermapId = curScene.lastMapId;
					enterx = curScene.lastx;
					entery = curScene.lasty;
					enterz = curScene.lastz;
				}
			}
			//不是动态场景，但是登录时是死亡状态，需要按照死亡死活的流程处理复活之后的地图和坐标
			else if (State_dead == curScene.curstate)
			{
				uint32_t reliveMapId = 0;
				uint32_t reliveSceneId = 0;
				Point3<float> relivePos(0.0, 0.0, 0.0);
				if (!g_GetReliveMgr()->GetDeadReliveInfoEx(entermapId, entersceneId, curpos, camp, killValue, reliveMapId, reliveSceneId, relivePos))
				{
					LogInfoFmtPrint("[center] CenterSceneMgr::EnterGameReq...GetDeadReliveInfoEx failed..cid:%lu,entermap:%u, scene:%u,camp:%d,killvalue:%d ", charId, entermapId, entersceneId, camp, killValue);
				}
				else
				{
					LogInfoFmtPrint("[center] CenterSceneMgr::EnterGameReq...GetDeadReliveInfoEx sucess..cid:%lu,entermap:%u, scene:%u,curpos:%f,%f,%f, camp:%d,killvalue:%d,relivemap:%u,relivepos:%f,%f,%f ", charId, entermapId, entersceneId, curpos.x, curpos.y, curpos.z, camp, killValue, reliveMapId, relivePos.x, relivePos.y, relivePos.z);
					//如果复活的地图和当前地图一样，只需要重置当前坐标为复活坐标即可，如果不一样，需要重置地图ID和场景ID
					enterx = relivePos.x;
					entery = relivePos.y;
					enterz = relivePos.z;
					if (entermapId != reliveMapId)
					{
						entersceneId = reliveMapId;
						entermapId = reliveMapId;
					}
				}
			}
			//是否需要重置
			bool resetFlag = false;
			//这里判断下待进入的场景是否存在
			CurCenterSceneInfo *pEnterScene = g_GetCenterSceneMgr()->GetCurSceneInfoBySceneId(entersceneId);
			if (nullptr == pEnterScene)
			{
				LogInfoFmtPrint("[center] CenterSceneMgr::EnterGameReq, nullptr == pEnterScene, cannt enterscene cid:%llu, sceneId:%u, dstScene:%u ", charId, entersceneId, curScene.lastSceneId);
				resetFlag = true; //场景不存在，需要重置
			}
			//这里再次验证下 上一个场景是否是动态场景，因为上一个场景有可能是工会或者工会地宫场景，而工会场景和工会地宫为动态场景
			else if (!g_GetCenterDuplicateMgr()->loginCanEnter(entersceneId, charId))
			{
				resetFlag = true; //登录不能进入，需要重置
			}
			if (resetFlag)
			{
				//登录不能进入，需要重置，这里重置成主城
				uint32_t mainCityMapId = g_GetMapCfgMgr()->GetMainCityMapId();
				const Point3<float> &mainCityPos = g_GetMapCfgMgr()->GetMainCityBornPos();
				CurCenterSceneInfo *pResetScene = g_GetCenterSceneMgr()->GetCurSceneInfoBySceneId(mainCityMapId);
				if (nullptr == pResetScene) //找不到主城场景，肯定出错了
				{
					LogErrFmtPrint("[center] EnterGameReq.....nullptr == pResetScene, can not find main city scene...uid:%u, cid:%llu,entersceneId:%u,entermapId:%u, sceneid:%u,mapid:%u, lastsceneid:%u, lastmapid:%u  ", uid, charId, entersceneId, entermapId, curScene.sceneId, curScene.mapId, curScene.lastSceneId, curScene.lastMapId);
					return false;
				}
				//进入地图和坐标设置成主城场景
				entersceneId = mainCityMapId;
				entermapId = mainCityMapId;
				enterx = mainCityPos.x;
				entery = mainCityPos.y;
				enterz = mainCityPos.z;
			}
			//最后再校验一次场景
			CurCenterSceneInfo* pdstScene = g_GetCenterSceneMgr()->GetCurSceneInfoBySceneId(entersceneId);
			if (nullptr == pdstScene)
			{
				LogErrFmtPrint("[center] EnterGameReq.....nullptr == pdstScene...uid:%u, cid:%llu,entersceneId:%u,entermapId:%u, sceneid:%u,mapid:%u, lastsceneid:%u, lastmapid:%u  ", uid, charId, entersceneId, entermapId, curScene.sceneId, curScene.mapId, curScene.lastSceneId, curScene.lastMapId);
				//这里做一个容错处理，如果还是找不到场景，就把角色放到 主城地图中
				uint32_t mainCityMapId = g_GetMapCfgMgr()->GetMainCityMapId();
				const Point3<float> &mainCityPos = g_GetMapCfgMgr()->GetMainCityBornPos();
				pdstScene = g_GetCenterSceneMgr()->GetCurSceneInfoBySceneId(mainCityMapId);
				if (nullptr == pdstScene) //找不到主城场景，肯定出错了
				{
					LogErrFmtPrint("[center] EnterGameReq 11.....nullptr == pdstScene, can not find main city scene...uid:%u, cid:%llu,entersceneId:%u,entermapId:%u, sceneid:%u,mapid:%u, lastsceneid:%u, lastmapid:%u  ", uid, charId, entersceneId, entermapId, curScene.sceneId, curScene.mapId, curScene.lastSceneId, curScene.lastMapId);
					return false;
				}
				//进入地图和坐标设置成主城场景
				entersceneId = mainCityMapId;
				entermapId = mainCityMapId;
				enterx = mainCityPos.x;
				entery = mainCityPos.y;
				enterz = mainCityPos.z;
			}
			//目标逻辑服ID
			dstLogicId = pdstScene->logicId;
			//这里新手引导副本是完成了
			finishFlag = 1;
		}

		

		CenterLoginLogicReq loginlogicReq;
		loginlogicReq.set_finish_flag(1);
		loginlogicReq.set_cid(charId);
		loginlogicReq.set_uid(uid);
		loginlogicReq.set_sceneid(entersceneId);
		loginlogicReq.set_mapid(entermapId);
		loginlogicReq.set_clientid(pUid->clientId);
		loginlogicReq.set_gateid(pUid->gateId);
		loginlogicReq.set_channelid(pUid->channelId);
		Vector3PB *protoPos = loginlogicReq.mutable_pos();
		protoPos->set_x(enterx);
		protoPos->set_y(entery);
		protoPos->set_z(enterz);
		CharLoginSyncCenterData *protoCenterData = loginlogicReq.mutable_center_data();
		if (nullptr != protoCenterData)
		{
			CharLoginCenterData(charId, *protoCenterData);
		}
		//新手引导副本字段
		loginlogicReq.set_finish_flag(finishFlag);
		loginlogicReq.set_finish_step(finishStep);
		loginlogicReq.set_guide_dup(guideDupId);
		CreateSceneParamProto*protoDup = loginlogicReq.mutable_dup_param();
		if (nullptr != protoDup)
		{
			protoDup->CopyFrom(paramProto);
		}
		//
		retCode = g_GetCenterService()->SendDataToLogic(dstLogicId, EMODULE_ID_LOGIN, CENTER_LOGIN_LOGIC_REQ, &loginlogicReq);
		if (!retCode)
		{
			LogErrFmtPrint("[center] EnterGameReq.....SendDataToLogic failed... senece own pdstScene->logicId:%u, uid:%u, charid:%llu, entersceneid:%u, entermapid:%u ", dstLogicId, uid, charId, entersceneId, entermapId);
			return false;
		}

		pUid->state = UID_STATE_ENTER;
		pUid->stateTimeStamp = g_GetGlobalServerTime()->Tick();
		pUid->playCid = charId;
		pUid->logicId = dstLogicId;
		LogInfoFmtPrint("[center] client enter game req ok, uid:%u, charid:%llu, logicid:%u, sceneid:%u ,mapid:%u ", pUid->uid, charId, dstLogicId, entersceneId, entermapId);
	}
	result = true;
Exit0:
	return result;
}

bool CenterAccountMgr::EnterGameRsp(RECV_PACKAGE& package)
{
	bool result = false;
	bool retCode = false;
	MMO_PROCESS_ERROR(package.session != nullptr);
	{
		CenterLoginLogicRsp loginLogicrsp;
		retCode = loginLogicrsp.ParseFromArray(package.dataAfterHead, package.sizeAfterHead);
		MMO_PROCESS_ERROR(retCode);

		uint32_t logicId = package.session->GetUserData().v32;
		CharIDType charId = loginLogicrsp.cid();
		uint32_t uid = loginLogicrsp.uid();
		uint32_t oldClientId = loginLogicrsp.clientid();
		uint32_t newClientId = GetClientId(uid);

		//如果进入游戏中的clientId跟目前最新的clientId不同 那么表示有正在挤号操作了 
		//这种情况直接上一个clientId进入游戏了 让最新的clientId去挤掉上一个旧的clientId
		if (oldClientId != newClientId)
		{
			GateChangeLogic(oldClientId, NotifyGateChangeLogic_cType_LEAVE_LOGIC, 0, true,LOGOUT_FLAG_REPLACE);
			MMOLOG_FMT_ERROR("EnterGameRsp fail because oldClientId != newClientId, oldClientId:%d, newClientId:%d", oldClientId, newClientId);
			MMO_PROCESS_ERROR(false);
		}

		//这里如果找不到pUid指针 那么直接通知逻辑服把对应的角色下线了
		UidGameInfo* pUid = GetUidGameInfo(oldClientId);
		if (pUid == nullptr)
		{
			NotifyLogicLeave(charId, uid, oldClientId, logicId, LOGOUT_TYPE_KICK_OUT);
			m_logoutUidMap[uid] = oldClientId;
			MMOLOG_FMT_ERROR("EnterGameRsp, cannt find uidinfo, uid:%d, charId:%llu", uid, charId);
			MMO_PROCESS_ERROR(false);
		}

		//这种出错是不会出现的 只是做一下容错
		if (pUid->state != UID_STATE_ENTER || pUid->playCid != charId)
		{
			GateChangeLogic(oldClientId, NotifyGateChangeLogic_cType_LEAVE_LOGIC, 0, true, LOGOUT_FLAG_KICK);
			NotifyLogicLeave(charId, uid, oldClientId, logicId, LOGOUT_TYPE_KICK_OUT);
			pUid->state = UID_STATE_LOGOUT;
			pUid->stateTimeStamp = g_GetGlobalServerTime()->Tick();
			m_logoutUidMap[uid] = oldClientId;
			MMOLOG_FMT_ERROR("EnterGameRsp, pUid->state != UID_STATE_ENTER || pUid->playCid != charId , uid:%d, state:%u, charId:%llu, oldcharid:%llu ", uid, pUid->state,charId,pUid->playCid);
			MMO_PROCESS_ERROR(false);
		}

		retCode = GateChangeLogic(pUid->clientId, NotifyGateChangeLogic_cType_ENTER_LOGIC, logicId);
		MMO_PROCESS_ERROR(retCode);


		LogInfoFmtPrint("[center] EnterGameRsp...client enter game logic rsp ok, charid:%llu, uid:%u,logicid:%u, oldlogicid:%u,  ", charId, uid, logicId,pUid->logicId);

		pUid->state = UID_STATE_GAMING;
		pUid->stateTimeStamp = g_GetGlobalServerTime()->Tick();
		pUid->playCid = charId;
		pUid->logicId = logicId;

		//g_GetCenterSceneMgr()->LoginGame(pUid->cidSceneIdMap[charId].sceneId);
		m_cidClientLogicIdMap[charId].clientId = pUid->clientId;
		m_cidClientLogicIdMap[charId].logicId = logicId;
		m_cidClientLogicIdMap[charId].cid = charId;
		m_cidClientLogicIdMap[charId].uid = uid;
		
		//
		g_GetCenterSceneMgr()->EnterLogic(logicId);
		//登录
		g_GetActiveCharacterMgr()->OnCharacterLogin(loginLogicrsp, pUid->channelId);
		
	}

	

	result = true;
Exit0:
	return result;
}

bool CenterAccountMgr::LeaveGameReq(uint32_t clientId, RECV_PACKAGE& package)
{
	bool result = false;
	bool retCode = false;

	LeaveGame  leave;
	retCode = leave.ParseFromArray(package.dataAfterHead, package.sizeAfterHead);
	MMO_PROCESS_ERROR(retCode);
	{
		CharIDType cid = leave.charid();
		UidGameInfo* pUid = GetUidGameInfo(clientId);
		if (pUid == nullptr)
		{
			MMOLOG_FMT_ERROR("LeaveGameReq, cannt find uidinfo, clientId:%d, cid:%llu", clientId, cid);
			MMO_PROCESS_ERROR(false);
		}
		LogDebugFmtPrint("[center] CenterAccountMgr::LeaveGameReq.....clientId:%u, cid:%llu, logicid:%u, state:%d  ", clientId, cid, pUid->logicId, pUid->state);

		pUid->state = UID_STATE_LOGOUT;
		retCode = NotifyLogicLeave(cid, pUid->uid, pUid->clientId, pUid->logicId,LOGOUT_TYPE_LOGOUT);
		MMO_PROCESS_ERROR(retCode);
	}

	result = true;
Exit0:
	return result;
}

bool CenterAccountMgr::LeaveGameRsp(RECV_PACKAGE& package)
{
	bool result = false;
	bool retCode = false;
	{
		CenterLoginOutLogicSceneRsp logicout;
		retCode = logicout.ParseFromArray(package.dataAfterHead, package.sizeAfterHead);
		MMO_PROCESS_ERROR(retCode);

		//uint32_t uid = logicout.uid();
		//CharIDType cid = logicout.cid();
		uint32_t clientId = logicout.clientid();
		
		UidGameInfo *pUid = GetUidGameInfo(clientId);
		if (pUid == nullptr)
		{
			//这里只是打印下日志，便于后面定位问题
			//LogInfoFmtPrint("[center] LeaveGameRsp....uid:%lu, cid:%lu, logicid:%u, state:%d ,gate:%d,clientid:%u, reqclientid:%u, ", pUid->uid, pUid->playCid, pUid->logicId, pUid->state, pUid->gateId, pUid->clientId, clientId);
			LogInfoFmtPrint("CenterAccountMgr::LeaveGameRsp pUid=Null,clientid=%d,cid:%lu", clientId, logicout.cid());
		}
		//下线流程
		Logout(clientId,logicout.cid());

	}
	result = true;
Exit0:
	return result;
}

bool CenterAccountMgr::LogicKickPlayer(RECV_PACKAGE& package)
{
	
	bool result = false;
	bool retCode = false;
	{
		LogicToCenterKickPlayerReq req;
		retCode = req.ParseFromArray(package.dataAfterHead, package.sizeAfterHead);
		MMO_PROCESS_ERROR(retCode);
		UidGameInfo* pUidInfo=GetUidGameInfoByCid(req.cid());
		MMO_PROCESS_ERROR(pUidInfo != nullptr);
		KickAccount(pUidInfo->uid,true);
	}
	result = true;
Exit0:
	return result;
}

bool CenterAccountMgr::Logout(uint32_t clientId, uint64_t cid)
{
	//将在线的角色做下线操作
	UidGameInfo* pUid = nullptr;
	bool retCode = true;
	pUid = GetUidGameInfo(clientId);
	if (pUid == nullptr)
	{
		MMOLOG_FMT_ERROR("LeaveGameRsp, cannt find uidinfo, clientId:%d",clientId);
		return false;
	}

	uint32_t newClientId = GetClientId(pUid->uid);

	//必须是离线状态
	if (pUid->state == UID_STATE_LOGOUT && pUid->playCid > 0)
	{
		//断线离开的帐号收到逻辑服离开场景消息后直接清除帐号信息
		//通知网关断开
		Leave(*pUid, false,cid);
	}
	else if (pUid->state == UID_STATE_DISCONNECT)
	{
		//角色下线
		Leave(*pUid, true,cid);
	}
	else
	{
		LogErrFmtPrint("[center] CenterAccountMgr::Logout..clientId:%u,uid:%u, cid:%lu,playCid:%lu, state:%d,newClientId:%u ",clientId,pUid->uid,cid,pUid->playCid,pUid->state,newClientId);
	}

	//如果当前还有最新的角色正在等待请求角色列表 那么进行新的请求
	if (clientId != newClientId)
	{
		UidGameInfo* pNewUid = GetUidGameInfo(newClientId);
		if (pNewUid && pNewUid->state == UID_STATE_LOGIN && pNewUid->playCid <= 0)
		{
			uint32_t zid = g_GetCenterService()->GetZoneId();
			retCode = CharLstReqToDB(zid, pNewUid->uid, pNewUid->clientId);
		}
	}

	return retCode;
}

//中心服踢角色 accountUid:账号UID
bool CenterAccountMgr::KickAccount(uint64_t accountUid, bool isCheatFlag)
{
	uint32_t kickClientId = GetClientId(accountUid);
	UidGameInfo* pKickUid = GetUidGameInfo(kickClientId);
	if (nullptr == pKickUid)
	{
		LogErrFmtPrint("[center] CenterAccountMgr::KickPlayer....nullptr == pKickUid...uid:%lu, clientid:%u ",accountUid,kickClientId);
		return false;
	}

	LOGOUT_FLAG leaveFlag = LOGOUT_FLAG_OPERATE_KICK;  //默认管理后面被T
	if (isCheatFlag)
	{
		leaveFlag = LOGOUT_FLAG_CHEAR_KICK;
	}

	//掉线重登或者被挤, 通知逻辑服退出
	//如果旧角色处于进入游戏 游戏中 切换场景中 断线中 四种状态那么需要通知旧角色从逻辑服退出 并且需要将oldUid作移除操作
	if (pKickUid->state == UID_STATE_ENTER ||
		pKickUid->state == UID_STATE_GAMING ||
		pKickUid->state == UID_STATE_SWITCH ||
		pKickUid->state == UID_STATE_DISCONNECT)
	{

		//如果旧的游戏角色不处于断线状态 那么需要通知网关 将角色的客户端session断开
		if (pKickUid->state != UID_STATE_DISCONNECT)
		{
			//强制断开客户端session
			if (!GateChangeLogic(pKickUid->clientId, NotifyGateChangeLogic_cType_LEAVE_LOGIC, 0, true, leaveFlag))
			{
				LogErrFmtPrint("[center] CenterAccountMgr::KickPlayer...current account state not allow kick player.. uid:%u, kick_player_cid:%llu, logic_id:%u, kickClientId:%u,clientId:%u, state:%u", pKickUid->uid, pKickUid->playCid, pKickUid->logicId, kickClientId, pKickUid->clientId, pKickUid->state);
				return false;
			}			
		}

		LogInfoFmtPrint("[center] CenterAccountMgr::KickPlayer...kick player logout.. uid:%u, kick_player_cid:%llu, logic_id:%u, kickClientId:%u,clientId:%u, state:%u", pKickUid->uid, pKickUid->playCid, pKickUid->logicId, kickClientId, pKickUid->clientId, pKickUid->state);
		//通知逻辑服角色下线
		NotifyLogicLeave(pKickUid->playCid, pKickUid->uid, pKickUid->clientId, pKickUid->logicId, LOGOUT_TYPE_KICK_OUT);
		pKickUid->state = UID_STATE_LOGOUT;
		//防止逻辑服没有回来中心服做账号回收
		pKickUid->stateTimeStamp = g_GetGlobalServerTime()->Tick();
		//将在离线的角色clientId放到离线列表中
		m_logoutUidMap[accountUid] = pKickUid->clientId;

		return true;
	}
	else
	{
		LogErrFmtPrint("[center] CenterAccountMgr::KickPlayer...current account state not allow kick player.. uid:%u, kick_player_cid:%llu, logic_id:%u, kickClientId:%u,clientId:%u, state:%u", pKickUid->uid, pKickUid->playCid, pKickUid->logicId, kickClientId, pKickUid->clientId, pKickUid->state);
	}

	return false;
}

bool CenterAccountMgr::KickAllAccount()
{
    bool result = true;
    bool retCode = false;

	CID_GAME_MAP::iterator iter = m_clientIdMap.begin();
	CID_GAME_MAP::iterator iterEnd = m_clientIdMap.end();

	for (; iter != iterEnd; iter++)
	{
		retCode = KickAccount(iter->second.uid);
		if (!retCode)
		{
			LogErrFmtPrint("kick account uid:%d, err", iter->second.uid);
			result = false;
		}
	}

	return result;
}

//逻辑服崩溃
void CenterAccountMgr::LogicCrash(uint32_t logicId)
{
	//过滤所有需要处理的帐号信息
	CID_GAME_MAP mapUidInfo;
	mapUidInfo.clear();
	CID_GAME_MAP::iterator iterClient = m_clientIdMap.begin();
	CID_GAME_MAP::iterator iterClientEnd = m_clientIdMap.end();
	for (; iterClient != iterClientEnd; ++iterClient)
	{
		uint32_t clientId = iterClient->first;
		UidGameInfo &uidinfo = iterClient->second;
		if (logicId != uidinfo.logicId)
		{
			continue;
		}
		mapUidInfo[clientId] = uidinfo;
	}
	//处理
	CID_GAME_MAP::iterator iterUid = mapUidInfo.begin();
	while (iterUid != mapUidInfo.end())
	{
		uint32_t clientId = iterUid->first;
		UidGameInfo &uidinfo = iterUid->second;
		//通知网关断开连接,如果已经关闭了，这个函数是不会再发送一次的
		GateChangeLogic(clientId, NotifyGateChangeLogic_cType_LEAVE_LOGIC, 0, true, LOGOUT_FLAG_REPLACE);
		//移除clientid
		m_clientIdMap.erase(clientId);
		//移除当前uid的client map, clientid - uid
		if (clientId == GetClientId(uidinfo.uid))
		{
			m_uidMap.erase(uidinfo.uid);
		}
		//移除正在下线的 uid-clientid
		if (clientId == GetLogoutClientId(uidinfo.uid))
		{
			m_logoutUidMap.erase(uidinfo.uid);
		}
		//需要下线的玩家
		if (uidinfo.playCid > 0)
		{
			//移除charid信息
			m_cidClientLogicIdMap.erase(uidinfo.playCid);
			//角色下线
			g_GetActiveCharacterMgr()->OnCharacterLogout(uidinfo.playCid);
		}
		//
		++iterUid;
	}
	//清空中间数据
	mapUidInfo.clear();
}

bool CenterAccountMgr::Leave(UidGameInfo& pUid, bool isKick, uint64_t cid)
{
	if (!isKick)
	{
		GateChangeLogic(pUid.clientId, NotifyGateChangeLogic_cType_LEAVE_LOGIC, 0, true);
	}

	//角色下线
	g_GetActiveCharacterMgr()->OnCharacterLogout(cid);
	//g_GetCenterSceneMgr()->LeaveGame(pUid.cidSceneIdMap[pUid.playCid].sceneId);
	//
	g_GetCenterSceneMgr()->LeaveLogic(pUid.logicId);

	uint32_t newClientId = GetClientId(pUid.uid);

	//如果最新上线的跟掉线的clientId一样 说明是同一个人 如果是同一个人需要对m_uidMap做移除操作
	if (pUid.clientId == newClientId)
	{
		m_logoutUidMap.erase(pUid.uid);
		m_uidMap.erase(pUid.uid);
		m_cidClientLogicIdMap.erase(cid);
	}	
	else 
	{
		if (cid != pUid.playCid)
		{
			m_cidClientLogicIdMap.erase(cid);
		}
	}

	//这里要放到前面，因为后面有erase操作
	m_clientIdMap.erase(pUid.clientId);
	
	return true;
}

bool CenterAccountMgr::DisConnect(RECV_PACKAGE& package)
{
	bool result = false;
	bool retCode = false;
	{
		GateNotifyDisConnect disconnect;
		retCode = disconnect.ParseFromArray(package.dataAfterHead, package.sizeAfterHead);
		MMO_PROCESS_ERROR(retCode);
		uint32_t clientId = disconnect.clientid();
		

		//如果此处找不到pUid 说明该角色已经下线了
		UidGameInfo* pUid = GetUidGameInfo(clientId);
		if (nullptr == pUid)
		{
			//做下安全操作 再做一次移除操作
			LogDebugFmtPrint("[center] CenterAccountMgr::DisConnect but nullptr == pUid....clientid:%u", clientId);
			m_clientIdMap.erase(clientId);
			goto Exit0;
		}

		uint64_t cid = pUid->playCid;
		LogInfoFmtPrint("[center] CenterAccountMgr::DisConnect uid%d, cid:%lu, state:%d, logicid:%u, clientid:%u", pUid->uid,pUid->playCid,pUid->state,pUid->logicId, clientId);

		//标记一下该连接网关已经关闭了
		pUid->isNotifyCloseGate = true;

		//如果该账号已经在下线中了 那么无需处理该流程
		if (pUid->state == UID_STATE_LOGOUT && pUid->playCid > 0)
		{
			LogErrFmtPrint("[center] CenterAccountMgr::DisConnect but pUid->state == UID_STATE_LOGOUT && pUid->playCid > 0....clientid:%u, oldclientid:%u,uid:%u, cid:%llu, state:%d,logicid:%u ", clientId, pUid->clientId, pUid->uid, pUid->playCid, pUid->state, pUid->logicId);
			goto Exit0;
		}

		//进入游戏，游戏中，切换场景中 掉线的话，进入重连倒计时，其他状态一律删除uid信息
		//如果当前角色处于一下状态 那么需要把角色做断线操作
		if ((pUid->state == UID_STATE_ENTER
			|| pUid->state == UID_STATE_GAMING
			|| pUid->state == UID_STATE_SWITCH) 
			&& pUid->playCid > 0
			)
		{
			//如果 角色 处于切换场景状态，需要通知目标逻辑节点断开连接，这个时候网关那边记录的有可能还是角色旧的逻辑节点ID
			//角色处于进游戏状态，这时还没有通知到网关角色所在的逻辑节点
			//中心服记录的是最新玩家的逻辑服ID,所以断开连接时统一由中心服通知逻辑服
			CL_NotifyDisconnectRsp notifyDisRsp;
			notifyDisRsp.set_clientid(clientId);
			notifyDisRsp.set_cid(pUid->playCid);
			if (!g_GetCenterService()->SendDataToLogic(pUid->logicId, EMODULE_ID_SERV_SYS, CENTER_TO_LOGIC_NOTIFY_DISCONNECT_RSP, &notifyDisRsp))
			{
				LogErrFmtPrint("[center] CenterAccountMgr::DisConnect...SendDataToLogic failed...uid:%u, cid:%lu, state:%d ",pUid->uid,pUid->playCid,pUid->state);
			}

			pUid->state = UID_STATE_DISCONNECT;
			pUid->stateTimeStamp = g_GetGlobalServerTime()->Tick();
		}
		//否则直接移除
		else
		{
			//如果还在线
			uint32_t uid = pUid->uid;
			uint32_t newClientId = GetClientId(uid);
			//如果最新上线的跟掉线的clientId一样 说明是同一个人 如果是同一个人需要对m_uidMap做移除操作
			if (clientId == newClientId)
			{
				m_uidMap.erase(uid);
				m_cidClientLogicIdMap.erase(pUid->playCid);
			}
			//如果clientId不一样 说明有可能不是同一个人
			else
			{
				UidGameInfo* pNewUid = GetUidGameInfo(newClientId);
				//不是同一个人 那么需要对m_uidMap做移除操作  如果相同则不需要做移除
				if (nullptr != pNewUid && pNewUid->playCid != cid)
				{
					m_cidClientLogicIdMap.erase(cid);
				}
			}
			
			m_clientIdMap.erase(clientId);
		}

		result = true;

		if (cid > 0)
			g_GetActiveCharacterMgr()->OnCharacterDisconnect(cid);

	}
Exit0:
	return result;
}

bool CenterAccountMgr::ReConnectReq(uint32_t clientId, RECV_PACKAGE& package)
{
	bool result = false;
	bool retCode = false;
	CenterToClientReconnctResultRsp reconnectRsp;
	ClientReConnectReq reconncet;
	retCode = reconncet.ParseFromArray(package.dataAfterHead, package.sizeAfterHead);
	MMO_PROCESS_ERROR(retCode);


	MMO_PROCESS_ERROR(package.session != nullptr);
	{
		uint32_t uid = reconncet.uid();
		uint64_t cid = reconncet.cid();
		//fix by lian
		//这里的oldClientId有两种情况 第一种是之前旧的clientId 第二种情况是有可能是新登录上来挤号的clientId
		uint32_t oldClientId = GetClientId(uid);
		UidGameInfo* pUid = GetUidGameInfo(oldClientId);
		if (pUid == nullptr || oldClientId == clientId)
		{
			reconnectRsp.set_result(RET_FAIL);
			MMOLOG_FMT_ERROR("[center] ReConnectReq, cannt find uidinfo, uid:%d, cid:%llu,oldclientid=%d,newclientid=%d", uid, cid,oldClientId,clientId);
			MMO_PROCESS_ERROR(false);
		}

		//将之前的断开
		GateChangeLogic(oldClientId, NotifyGateChangeLogic_cType_LEAVE_LOGIC, 0, true, LOGOUT_FLAG_NORMAL);
		pUid->isNotifyCloseGate = false;

		//如果重连过程中跟最新登录的玩家ID不同那么直接不管 如果cid相同情况下 判断上一个连接的状态没有处于掉线状态 那么只需要简单的把网管断开
		//有时候断线，服务器没检测到，所以这里只要是在游戏中，收到这条消息，都作断线处理  add by wangxx 
		if (pUid->state < UID_STATE_ENTER || pUid->playCid != cid || pUid->playCid == 0)
		{
			GateChangeLogic(clientId, NotifyGateChangeLogic_cType_LEAVE_LOGIC, 0);
			reconnectRsp.set_result(RET_FAIL);
			MMOLOG_FMT_ERROR("[center] ReConnectReq, pUid->state != UID_STATE_DISCONNECT || pUid->playCid != cid... uid:%u, cid:%llu, oldcid:%llu, logicid:%u, state:%d ", uid, cid, pUid->playCid, pUid->logicId, pUid->state);
			MMO_PROCESS_ERROR(false);
		}

		////如果上一步验证通过 那么认为是同一个角色
		//uint64_t tick = g_GetGlobalServerTime()->Tick();
		//int32_t difftime = (int32_t)(tick - pUid->stateTimeStamp);

		////if (pUid->token != reconncet.token() || difftime > RECONNECT_TIME)
		//MMO_PROCESS_ERROR(difftime < RECONNECT_TIME);

		//pUid->clientId = clientId;

		
		int32_t oldState = pUid->state;
		if (pUid->playCid > 0)
		{
			pUid->state = UID_STATE_GAMING;
			if (oldClientId!=clientId)
			{
				m_uidMap[pUid->uid] = clientId;
				m_clientIdMap[clientId] = *pUid;
				m_clientIdMap[clientId].clientId = clientId;
				m_clientIdMap[clientId].isNotifyCloseGate = 0;
				m_clientIdMap[clientId].playCid = pUid->playCid;
				m_cidClientLogicIdMap[pUid->playCid].clientId = clientId;
				m_cidClientLogicIdMap[pUid->playCid].cid = pUid->playCid;
				m_cidClientLogicIdMap[pUid->playCid].logicId = pUid->logicId;
				m_cidClientLogicIdMap[pUid->playCid].uid = pUid->uid;


				
				GateChangeLogic(clientId, NotifyGateChangeLogic_cType_ENTER_LOGIC, pUid->logicId);
				//通知逻辑服重连
				CL_NotifyReconnectSucceedRsp reconnecRsp;
				reconnecRsp.set_cid(pUid->playCid);
				reconnecRsp.set_clientid(clientId);
				if (!g_GetCenterService()->SendDataToLogic(pUid->logicId, EMODULE_ID_SERV_SYS, CENTER_TO_LOGIC_NOTIFY_RECONNECT_SUCCEED, &reconnecRsp))
				{
					LogErrFmtPrint("[center] CenterAccountMgr::ReConnectReq...SendDataToLogic failed...uid:%u, cid:%lu, state:%d,cid:%lu,logicid:%d, oldState:%d ", pUid->uid, pUid->playCid, pUid->state, pUid->playCid, pUid->logicId, oldState);
				}


				g_GetActiveCharacterMgr()->onCharacterReconnect(m_clientIdMap[clientId].playCid);

				reconnectRsp.set_result(RET_SUCCESS);

				m_clientIdMap.erase(oldClientId);
			}
		}
		else
		{
			reconnectRsp.set_result(RET_FAIL);
		}
		//else
		//{
		//	pUid->state = UID_STATE_LOGIN;
		//	pUid->stateTimeStamp = tick;
		//}
		LogInfoFmtPrint("[center] reconnect result=%d,oldClientId=%d,newclientid=%d,uid:%u, cid:%lu, state:%d,logicid:%d,oldState:%d", reconnectRsp.result(), oldClientId, clientId, pUid->uid, pUid->playCid, pUid->state, pUid->logicId, oldState);
	}
	result = true;



Exit0:
	//if (reconnectRsp.result() != RET_SUCCESS)
	{
		g_GetCenterService()->SendDataToClient(reconncet.cid(), EMODULE_ID_LOGIN, CENTER_TO_CLIENT_RECONNECT_RESUTL, &reconnectRsp);
	}
	return result;
}

bool CenterAccountMgr::NotifyLogicLeave(CharIDType cid, uint32_t uid)
{
	bool result = false;
	bool retCode = false;
	uint32_t logicId = GetLogicIdByCid(cid);

	GateLoginOutLogicSceneReq logicOut;
	logicOut.set_cid(cid);
	logicOut.set_uid(uid);
	retCode = g_GetCenterService()->SendDataToLogic(logicId, EMODULE_ID_LOGIN, CENTER_LOGINOUT_LOGIC_REQ, &logicOut);
	MMO_PROCESS_ERROR(retCode);

	result = true;
Exit0:
	return result;
}

bool CenterAccountMgr::NotifyLogicLeave(CharIDType cid, uint32_t uid, uint32_t clientId, uint32_t logicId, LOGOUT_TYPE type /*= LOGOUT_TYPE_NONE*/)
{
	bool result = false;
	bool retCode = false;

	GateLoginOutLogicSceneReq logicOut;
	logicOut.set_cid(cid);
	logicOut.set_uid(uid);
	logicOut.set_type(type);
	logicOut.set_clientid(clientId);
	retCode = g_GetCenterService()->SendDataToLogic(logicId, EMODULE_ID_LOGIN, CENTER_LOGINOUT_LOGIC_REQ, &logicOut);
	MMO_PROCESS_ERROR(retCode);

	result = true;
Exit0:
	return result;
}

void CenterAccountMgr::OnChangeScene(CharIDType cid, uint32_t clientId, uint32_t uid, uint32_t logicId)
{

	UidGameInfo *pUid = GetUidGameInfo(clientId);
	if (nullptr == pUid)
	{
		LogErrFmtPrint("[center] CenterAccountMgr::OnChangeScene...nullptr == pUid....cid:%llu, uid:%u, logicId:%u  ", cid, uid, logicId);
		return;
	}
	LogDebugFmtPrint("[center] CenterAccountMgr::OnChangeScene...cid:%llu, uid:%u, logicId:%u, oldcid:%llu, oldLogicId:%u, oldstate:%u  ",cid,uid,logicId,pUid->playCid,pUid->logicId,pUid->state);

	pUid->logicId = logicId;//逻辑服ID需要更新
	if (pUid->state == UID_STATE_SWITCH)
	{
		if (pUid->playCid == cid)//只有是同一个角色切换场景之后才会更新到游戏状态
		{
			pUid->state = UID_STATE_GAMING;
			pUid->stateTimeStamp = g_GetGlobalServerTime()->Tick();
		}
	}
	//更新玩家所在的逻辑服
	CID_CLIENT_LOGIC_ID_MAP::iterator iter = m_cidClientLogicIdMap.find(cid);
	if (iter != m_cidClientLogicIdMap.end())
	{
		if (m_cidClientLogicIdMap[cid].logicId != logicId)
		{
			m_cidClientLogicIdMap[cid].logicId = logicId;
		}
	}
	
}

bool CenterAccountMgr::OnPrepareChangeScene(CharIDType cid, uint32_t clientId, uint32_t uid, uint32_t logicId)
{
	UidGameInfo *pUid = GetUidGameInfo(clientId);
	if (nullptr == pUid)
	{
		LogErrFmtPrint("[center] CenterAccountMgr::OnPrepareChangeScene..... GetUidGameInfo return null.... uid:%u, cid:%llu, logicId:%u ", uid, cid, logicId );
		return false;
	}
	//通知网关，更换逻辑服ID
	if (!GateChangeLogic(pUid->clientId, NotifyGateChangeLogic_cType_TTRANS_LOGIC, logicId))
	{
		LogErrFmtPrint("[center] CenterAccountMgr::OnPrepareChangeScene..... GateChangeLogic failed.... uid:%u, oldclientid:%u, cid:%llu, logicId:%u, oldlogicid:%u, oldsate:%d", uid, pUid->clientId, cid,logicId, pUid->logicId, pUid->state);
		return false;
	}
	//
	g_GetCenterSceneMgr()->ChangeLogic(pUid->logicId, logicId);

	LogDebugFmtPrint("[center] CenterAccountMgr::OnPrepareChangeScene.....uid:%u, oldclientid:%u, cid:%llu, logicId:%u, oldlogicid:%u, oldsate:%d", uid, pUid->clientId, cid, logicId, pUid->logicId, pUid->state);

	pUid->playCid = cid;
	pUid->state = UID_STATE_SWITCH;
	pUid->stateTimeStamp = g_GetGlobalServerTime()->Tick();
	pUid->logicId = logicId;

	//更新玩家所在的逻辑服
	CID_CLIENT_LOGIC_ID_MAP::iterator iter = m_cidClientLogicIdMap.find(cid);
	if (iter != m_cidClientLogicIdMap.end())
	{
		if (m_cidClientLogicIdMap[cid].logicId != logicId)
		{
			m_cidClientLogicIdMap[cid].logicId = logicId;
		}
	}

	return true;
}

bool CenterAccountMgr::GateChangeLogic(uint32_t clientId, uint32_t ctype, uint32_t logicId, bool force /* = false */, LOGOUT_FLAG flag)
{
	UidGameInfo* pUidGameInfo = GetUidGameInfo(clientId);
	if (nullptr == pUidGameInfo)
	{
		return true;
	}

	//已经通知过网关关闭连接了
	if (pUidGameInfo->isNotifyCloseGate)
	{
		LogDebugFmtPrint("notice gate change logic or disale type=%d,clientid=%d,isnotify=%d", ctype, clientId, pUidGameInfo->isNotifyCloseGate);
		return true;
	}

	//通知客户端强制断线
	//路由管理器移除对应的标识
	NotifyGateChangeLogic notify;
	notify.set_ctype(NotifyGateChangeLogic_cType(ctype));
	notify.set_clientid(clientId);
	notify.set_cid(pUidGameInfo->playCid);
	notify.set_uid(pUidGameInfo->uid);

	if (force)
	{
		m_cidClientLogicIdMap.erase(pUidGameInfo->playCid);
		pUidGameInfo->isNotifyCloseGate = true;
		notify.set_forceleave(true);
		notify.set_leaveflag(flag);
	}
	if (logicId > 0)
		notify.set_logicid(logicId);

	LogDebugFmtPrint("notice gate change logic or disale type=%d,clientid=%d", ctype, clientId);

	return g_GetCenterService()->SendDataToGate(pUidGameInfo->gateId, EMODULE_ID_SERV_SYS, CENTER_NOTIFY_GATE_CHANGE_LOGIC, &notify);
}

uint32_t CenterAccountMgr::GetClientIdByCid(CharIDType cid)
{
	CID_CLIENT_LOGIC_ID_MAP::iterator iter = m_cidClientLogicIdMap.find(cid);
	CID_CLIENT_LOGIC_ID_MAP::iterator iterEnd = m_cidClientLogicIdMap.end();
	if (iter == iterEnd)
		return 0;
	return iter->second.clientId;
}

uint32_t CenterAccountMgr::GetLogicIdByCid(CharIDType cid)
{
	CID_CLIENT_LOGIC_ID_MAP::iterator iter = m_cidClientLogicIdMap.find(cid);
	CID_CLIENT_LOGIC_ID_MAP::iterator iterEnd = m_cidClientLogicIdMap.end();
	if (iter == iterEnd)
		return 0;
	return iter->second.logicId;
}

void CenterAccountMgr::GetClientLst(MAP_UINT32_LST_UINT32& outClientIds, SET_UINT64& setChars)
{
	auto iter = setChars.begin();
	auto iterEnd = setChars.end();
	//uint32_t clientId = 0;
	for (; iter != iterEnd; iter++)
	{
		//clientId = GetClientIdByCid(*iter);
		UidGameInfo* pUid = g_GetCenterAccountMgr()->GetUidGameInfoByCid(*iter);
		//这种情况不打印了错误了,因为会给一些没在线的玩家发送数据
		if (!pUid || pUid->state >= UID_STATE_LOGOUT || pUid->clientId <= 0)
		{
			//LogErrFmtPrint("CenterAccountMgr::GetClientLst, cannt find uid by use cid:%lu", *iter);
			continue;
		}
		LIST_UINT32& clientLst = outClientIds[pUid->gateId];
		clientLst.push_back(pUid->clientId);
		//outClientIds.push_back(pUid->clientId);
	}
}

void CenterAccountMgr::PlayerCreateLog(uint32_t uid, CharIDType cid, uint32_t channelId, const CharacterDBSimpleInfo& charSimpleInfo)
{
	rapidjson::Document doc;
	JsonObject obj(&doc);
	obj.AddUInt32("tid", TID_CREATE_CHARACTER);
	obj.AddUInt32("uid", uid);
	obj.AddUInt64("rid", cid);
	obj.AddUInt32("chanid", channelId);
	obj.AddUInt64("time", g_GetGlobalServerTime()->UnixSec());
	obj.AddString("rname", charSimpleInfo.name());
	obj.AddUInt32("rjob", charSimpleInfo.prof());
	obj.AddUInt32("rsex", charSimpleInfo.sex());
	obj.AddUInt32("race", charSimpleInfo.race());
	obj.AddUInt32("zid", g_GetCenterService()->GetZoneId());
	bool bsuc = g_GetCenterService()->SendLogToLogServer(obj);
	if (bsuc == false)
		LogErrFmtPrint("player create character log error, cid:%llu, uid:%d", cid, uid);
}
void CenterAccountMgr::PlayerDelCharacterLog(uint32_t uid, CharIDType cid, uint32_t channelId, string cName)
{
	rapidjson::Document doc;
	JsonObject obj(&doc);
	obj.AddUInt32("tid", TID_DEL_CHARACTER);
	obj.AddUInt32("uid", uid);
	obj.AddUInt64("rid", cid);
	obj.AddUInt32("chanid", channelId);
	obj.AddUInt64("time", g_GetGlobalServerTime()->UnixSec());
	obj.AddString("rname", cName.c_str());
	obj.AddUInt32("zid", g_GetCenterService()->GetZoneId());
	bool bsuc = g_GetCenterService()->SendLogToLogServer(obj);
	if (bsuc == false)
		LogErrFmtPrint("player del character log error, cid:%llu, uid:%d", cid, uid);
}

void CenterAccountMgr::PlayerDelCancelCharacterLog(uint32_t uid, CharIDType cid, uint32_t channelId, string cName)
{
	rapidjson::Document doc;
	JsonObject obj(&doc);
	obj.AddUInt32("tid", TID_DEL_CHARACTER_CANCEL);
	obj.AddUInt32("uid", uid);
	obj.AddUInt64("rid", cid);
	obj.AddUInt32("chanid", channelId);
	obj.AddUInt64("time", g_GetGlobalServerTime()->UnixSec());
	obj.AddString("rname", cName.c_str());
	obj.AddUInt32("zid", g_GetCenterService()->GetZoneId());
	bool bsuc = g_GetCenterService()->SendLogToLogServer(obj);
	if (bsuc == false)
		LogErrFmtPrint("player del character cancel log error, cid:%llu, uid:%d", cid, uid);
}

//玩家登录中心服玩家相关的属性数据
void CenterAccountMgr::CharLoginCenterData(CharIDType charId, CharLoginSyncCenterData &centerData)
{
	g_GetUnionMgr()->LoginSyncData(charId, &centerData);
	g_GetTeamMgr()->LoginSyncData(charId, &centerData);
	g_GetRoomMgr()->loginSyncData(charId, &centerData);
}

//是否所有的预加载都完成了
bool CenterAccountMgr::IsAllPreLoadFinished()
{
	if (g_GetOfflineCharacterDataMgr()->IsFinishLoad()
		&& g_GetMailMgr()->IsFinishLoad()
		&& g_GetUnionMgr()->IsFinishLoad()
		&& g_GetArenaMgr()->IsFinishLoad()
		&& g_GetGlobalMgr()->HasInit()
		&& g_GetMallMgr()->IsFinishLoad()
		)
	{
		return true;
	}
	return false;
}

void CenterAccountMgr::GetOnlineCidQueue(std::queue<CharIDType>& cidLst)
{
	CID_CLIENT_LOGIC_ID_MAP::iterator iter = m_cidClientLogicIdMap.begin();
	CID_CLIENT_LOGIC_ID_MAP::iterator iterEnd = m_cidClientLogicIdMap.end();

	std::queue<CharIDType> tmpcidList;
	for (; iter != iterEnd; iter++)
	{
		tmpcidList.push(iter->first);
	}
	cidLst.swap(tmpcidList);
}